#include "RingQueue.hpp"
#include "Task.hpp"
#include <pthread.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>


#define num 5

std::string SelfName()
{
    char name[128];
    snprintf(name,sizeof(name),"thread[0x%x]",pthread_self());
    return name;
}

std::string op = "+-*/%";

void *ProductorRoutine(void *rq)
{
    // RingQueue<int> * rq_ = static_cast<RingQueue<int> *>(rq);
    RingQueue<Task> *rq_ = static_cast<RingQueue<Task> *>(rq);
    
    while (true)
    {
        // version1
        // int data = rand() % 10 + 1;
        // rq_->push(data);
        // std::cout << "生产完成，生产的数据是：" << data << std::endl;

        // version2
        // 构建or获取任务 -- 这个是要花时间的
        int x = rand() % 10 + 1;
        int y = rand() % 10 + 1;
        char oper = op[rand() % num];
        Task t(x,y,oper,mymath);
        // 生产任务
        rq_->push(t);
        // 输出提示
        std::cout << SelfName() << "生产者派发了一个任务：" << t.toTaskString() << std::endl;

        // sleep(1);
    }
    return nullptr;
}

void *ConsumerRoutine(void *rq)
{
    // RingQueue<int> *rq_ = static_cast<RingQueue<int> *>(rq);
    RingQueue<Task> *rq_ = static_cast<RingQueue<Task> *>(rq);

    while (true)
    {
        // version1
        // sleep(1);
        // int data;
        // rq_->pop(&data);
        // std::cout << "消费完成，消费的数据是：" << data << std::endl;

        // version2
        // 消费任务
        // sleep(1);
        Task t;
        rq_->pop(&t);
        std::cout<< SelfName() << "消费者消费了一个任务..." << t() << std::endl;
    }
    return nullptr;
}

int main()
{
    srand((unsigned long)time(0));
    RingQueue<Task> *rq = new RingQueue<Task>();
    // 单生产，单消费 ，多生产，多消费 -- 只要保证，最终进入临界区是一个生产，一个消费就行！(维护好，生生互斥，消消互斥)
    // 多生产，多消费意义？构建工作，和消费任务是要花时间的！
    // pthread_t c, p;
    // pthread_create(&p, nullptr, ProductorRoutine, rq);
    // pthread_create(&c, nullptr, ConsumerRoutine, rq);

    pthread_t c[8], p[4];
    for(int i = 0;i < 8;i++) pthread_create(c+i, nullptr, ConsumerRoutine, rq);
    for(int i = 0;i < 4;i++) pthread_create(p+i, nullptr, ProductorRoutine, rq);

    for(int i = 0;i < 8;i++) pthread_join(c[i], nullptr);
    for(int i = 0;i < 4;i++) pthread_join(p[i], nullptr);
    // pthread_join(p, nullptr);
    // pthread_join(c, nullptr);
    delete rq;
    return 0;
}

/////////////////////////////////////POSIX信号量////////////////////////////////////////

// 1.之前代码不足之处
// a.一个线程，再操作临界资源的时候，必须临界资源是满足条件的！
// b.可是，公共资源是否满足生产或消费条件，我们无法直接得知(无法事前得知)
// c.只能先加锁，再检测，再操作，再解锁(因为你要检测，本质：也就在访问临界资源！)
// 实际情况可能存在：：一份公共资源，但允许同时访问不同的区域

// 2.引入信号量概念，是什么，为什么？ -- 信号量一般需要什么操作？
// 什么是信号量？
// a.信号量的本质是一把计数器(衡量临界资源中资源数量多少的计数器)
// c.只要拥有信号量，就未来一定能够拥有临界资源的一部分，申请信号量的本质：对临界资源中特定小快资源的 预定 机制！
// 所以有可能，我们在访问临界资源之前，我们其实就可以提前知道临界资源的使用情况！
// 只要申请成功，就一定有你的资源
// 只要申请失败，就说明条件不就绪，你只能等！！

// 线程要访问临界资源中的某一块区域 -- 申请信号量 -- 所哟人必须先看到信号量 -- 信号量本身必须是：公共资源
// 计数器 - 递减 && 递增 sem_t sem =10;
// sem--： -- 申请资源 -- 必须保证操作的原子性 -- P
// sem++： -- 归还资源 -- 必须保证操作的原子性 -- V
// 信号量的核心操作：PV原语

//////////////////////////////////////调用接口//////////////////////////////////////////////

// 初始化信号量
// #include <semaphore.h>
// int sem_init(sem_t *sem, int pshared, unsigned int value);
// 参数：
// pshared:0表示线程间共享，非零表示进程间共享
// value：信号量初始值

// 销毁信号量
// int sem_destroy(sem_t *sem);

// 等待信号量
// 功能：等待信号量，会将信号量的值减1
// int sem_wait(sem_t *sem); //P()

// 发布信号量
// 功能：发布信号量，表示资源使用完毕，可以归还资源了。将信号量值加1。
// int sem_post(sem_t *sem);//V()

//////////////////////////////////////环形队列 && cp问题//////////////////////////////////////////////

// 生产者和消费者在什么情况下可能访问同一个位置
// 1.空的时候
// 2.满的时候
// 3.其他情况，生产者和消费者，根本访问的就是不同区域！

// 规则(为了完成环形队列 && cp问题，我们要做的核心工作是什么？)
// 1.消费者不能超过生产者(生产者先运行)
// 2.生产者不能套多一个圈(满的时候，消费者先运行)
// 在环形队列中，大部分情况下，单消费和单生产时可以并发执行的！
// 只有在满，或者空的时候，才有互斥与同步问题！

// 怎么保证遵守规则？
// 信号量时用来衡量临界资源中资源数量的
// 1.对于生产者而言，看的是：队列中剩余的位置 -- 空间资源定义一个信号量
// 2.对于消费者而言，看的是：放入队列中的数据 -- 数据资源定义一个信号量

// 生产者：                                      消费者：
// productor_sem:10                             consumer_sem:10
// 申请成功，继续向下执行
// 申请失败，当前执行流，阻塞在申请处
// P(productor_sem);                            P(consumer_sem);
// 从事生产活动 -- 把数据放到队列中               从事消费活动
// V(consumer_sem);                             V(productor_sem);

// 未来生产和消费的位置要想清楚：
// 1.其实就是队列中的下标
// 2.一定是两个下标
// 3.为空或者为满，下标相同